package avicit.bdp.dds.api.service;


import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.dto.StorageResourceConf;
import avicit.bdp.common.service.dto.CommonTypeDTO;
import avicit.bdp.common.service.service.CommonTypeService;
import avicit.bdp.common.service.service.FileOperateCommonService;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.common.utils.CheckParaUtils;
import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.enums.CommonTypeEnums;
import avicit.bdp.dds.api.dto.ProcessNodeDto;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.common.utils.FileUtils;
import avicit.bdp.dds.dao.entity.*;
import avicit.bdp.dds.dao.mapper.ProcessDefinitionMapper;
import avicit.bdp.dds.dao.mapper.ProjectMapper;
import avicit.bdp.dds.dao.mapper.TaskNodesMapper;
import avicit.bdp.dds.dispatch.enums.TaskType;
import avicit.platform6.api.system.ConvertColumnClient;
import avicit.platform6.api.system.SysLookupClient;
import avicit.platform6.api.system.impl.SystemConstant;
import avicit.platform6.commons.utils.BusinessUtil;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.commons.utils.PojoUtil;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.properties.PlatformConstant;
import avicit.platform6.core.rest.msg.QueryRespBean;
import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xugb
 * 流程节点服务
 **/
@Service
public class TaskNodesService extends BaseService<TaskNodesMapper, TaskNodes> {

    private static final String BUILT_IN_ALGO = "系统内置";

    private static final String CUSTOMIZED_ALGO = "自定义";

    private static final String OTHER_ALGO = "其它";

    private static final String TASK_NODE_TYPE = "sparkFlow";

    private static final String DEFAULT_CODE = "SPARKFLOW";

    private static final String UPLOAD_FILE_END_WITH = ".py";// 上传算法文件当前仅支持.py格式

    private static final String REMARK_KEY_NAME = "getRemark";

    private static final String SETTING_KEY_NAME = "getSettingJson";

    @Autowired
    private CommonTypeService commonTypeService;

    @Autowired
    private ProcessDefinitionMapper processDefinitionMapper;

    @Autowired
    private AlgoNodesService algoNodesService;

    @Autowired
    private FileOperateCommonService fileOperateCommonService;

    @Autowired
    private ProjectMapper projectMapper;

    /**
     * 项目类型Service
     */
    @Autowired
    private ProjectTypeService projectTypeService;

    /**
     * 节点与类型映射关系Service
     */
    @Autowired
    private TaskNodeTypeService taskNodeTypeService;

    /**
     * 系统属性
     */
    @Autowired
    private ConvertColumnClient convertColumnClient;

    /**
     * V6平台通用代码
     */
    @Autowired
    private SysLookupClient sysLookupClient;

    public Map<String, List<ProcessNodeDto>> getProcessNode(String projectId, String processDefinitionId) {
        //   processNode--节点组件；tableNode--模型组件;algoNode--算法组件
        ProcessDefinition processDefinition = processDefinitionMapper.selectByPrimaryKey(processDefinitionId);
        String type = CommonTypeEnums.COMMON_FLOW;
        if (processDefinition != null && StringUtils.isNotBlank(processDefinition.getDatasourceId())) {
            type = processDefinition.getDatasourceId();
        }
        Map<String, List<ProcessNodeDto>> result = new HashMap<>(4);
        List<ProcessNodeDto> nodeList = getProcessNodeDto(type, 0);
        result.put("processNode", nodeList);
        if (type.equals(CommonTypeEnums.COMMON_FLOW)) {
            // tableNode
            List<ProcessNodeDto> tableList = getTableNodeDto(projectId);
            result.put("tableNode", tableList);
            //algoNode TODO 暂时不用数据开发工作的算法库
            //List<ProcessNodeDto> algoNodeList = getAlgoNodeDto(projectId, type);
            //result.put("algoNode", algoNodeList);
        } else if (type.startsWith("phm")) {
            //PHM工程的自定义算法组件
            List<ProcessNodeDto> algoNodeList = getProcessNodeDto(type, 1);
            result.put("algoNode", algoNodeList);
        } else {
            //algoNode
            List<ProcessNodeDto> algoNodeList = getSparkFlowAlgoNodeDto(projectId);
            result.put("algoNode", algoNodeList);
        }


        return result;
    }

    public List<ProcessNodeDto> getSparkFlowAlgoNodeDto(String projectId) {
        List<ProcessNodeDto> nodeList;
        List<CommonTypeDTO> typeList = commonTypeService.findCommonType(CommonTypeEnums.NODES, projectId);
        //去除顶级节点
        if (!typeList.isEmpty()) {
            typeList = typeList.get(0).getChildren();
            if (typeList == null) {
                typeList = new ArrayList<>();
            }
        }
        Project project = projectMapper.selectByPrimaryKey(projectId);

        List<Map<String, Object>> algoNodesList = this.mapper.getAlgoList(projectId, null, CommonTypeEnums.SPARK_FLOW);
        Map<String, List<ProcessNodeDto>> nodeMap = new HashMap<>(16);
        //处理流程节点
        for (Map<String, Object> node : algoNodesList) {
            ProcessNodeDto dto = new ProcessNodeDto();
            dto.setId(MapUtils.getString(node, "id"));
            dto.setName(MapUtils.getString(node, "name"));
            dto.setCode(TaskType.SPARKFLOW.name());
            dto.setIconName("algorithm");
            dto.setColor("#0000FF");
            String content = MapUtils.getString(node, "content", "");
            content = content.replaceAll("\n", "\\n");
            String params = MapUtils.getString(node, "params", "");
            if (StringUtils.isNotBlank(params)) {
                dto.setParams(params);
            } else {
                dto.setParams("{\n" +
                        "  \"name\": \"python自定义算法\",\n" +
                        "  \"bundle\": \"avicit.bdp.bundle.script.ExecutePythonAlgo\",\n" +
                        "  \"description\": \"python脚本执行，直接输出dataframe\",\n" +
                        "  \"inportType\": \"DEFAULT\",\n" +
                        "  \"inports\": [\"Default\"],\n" +
                        "  \"inportsName\": [\"输入\"],\n" +
                        "  \"outportType\": \"DEFAULT\",\n" +
                        "  \"outports\": [\"Default\"],\n" +
                        "  \"outportsName\": [\"输出\"],\n" +
                        "  \"outportsFieldType\":\"Common\",\n" +
                        "  \"outportsField\":\",schema+\",\n" +
                        "  \"properties\": [\n" +
//                        "    {\n" +
//                        "      \"name\": \"script\",\n" +
//                        "      \"displayName\": \"python脚本\",\n" +
//                        "      \"description\": \"编写python脚本,必须实现 def perform(dataList):  接口\",\n" +
//                        "      \"defaultValue\": \"" + content + "\",\n" +
//                        "      \"required\": true,\n" +
//                        "      \"language\": \"python\",\n" +
//                        "      \"component\": \"CodeEditor\"\n" +
//                        "    },\n" +
                        "    {\n" +
                        "      \"name\": \"schema\",\n" +
                        "      \"displayName\": \"列名\",\n" +
                        "      \"description\": \"数据处理后的列名，如果没有输出，则不用此参数\",\n" +
                        "      \"defaultValue\": \"" + params + "\",\n" +
                        "      \"required\": false,\n" +
                        "      \"language\": \"UserDefine\",\n" +
                        "      \"component\": \"UserAddColumn\"\n" +
                        "    }\n" +
                        "  ]\n" +
                        "}");
            }

            dto.setRemark(MapUtils.getString(node, "remark", ""));
            //是节点
            dto.setIsNode(1);
            dto.setType(dto.getCode());
            //算法节点
            dto.setAlgorithmType("0");
            if (nodeMap.containsKey(MapUtils.getString(node, "typeId"))) {
                List<ProcessNodeDto> list = nodeMap.get(MapUtils.getString(node, "typeId"));
                list.add(dto);
            } else {
                List<ProcessNodeDto> list = new ArrayList<>();
                list.add(dto);
                nodeMap.put(MapUtils.getString(node, "typeId"), list);
            }
        }
        nodeList = handleList(typeList, nodeMap);
        return nodeList;
    }


    public List<ProcessNodeDto> getAlgoNodeDto(String projectId, String type) {
        List<ProcessNodeDto> nodeList;
        List<CommonTypeDTO> typeList = commonTypeService.findCommonType(CommonTypeEnums.NODES, projectId);
        //去除分类的顶级节点
        if (!typeList.isEmpty() && typeList.get(0).getChildren() != null) {
            typeList = typeList.get(0).getChildren();
        } else {
            typeList = new ArrayList<>();
        }
        StorageResourceConf storageResourceConf = fileOperateCommonService.getStorageResourceConf(projectId);
        Project project = projectMapper.selectByPrimaryKey(projectId);
        String engineId = null;
        //数据开发工作台需要特别判断, 与存储路径相关
        if ("0".equals(project.getProjectType()) && storageResourceConf != null) {
            engineId = storageResourceConf.getId();
        }
        List<Map<String, Object>> algoNodesList = this.mapper.getAlgoList(projectId, engineId, type);
        Map<String, List<ProcessNodeDto>> nodeMap = new HashMap<>(16);
        //处理流程节点
        for (Map<String, Object> node : algoNodesList) {
            ProcessNodeDto dto = new ProcessNodeDto();
            dto.setId(MapUtils.getString(node, "id"));
            dto.setName(MapUtils.getString(node, "name"));
            dto.setCode(TaskType.ALGORITHM.name());
            dto.setIconName("algorithm");
            dto.setColor("#0000FF");
            dto.setParams(MapUtils.getString(node, "params", ""));
            //dto.setRemark(MapUtils.getString(node, "remark", ""));
            //是节点
            dto.setIsNode(1);
            dto.setType(TaskType.ALGORITHM.name());
            //算法库节点
            dto.setAlgorithmType("9");
            if (nodeMap.containsKey(MapUtils.getString(node, "typeId"))) {
                List<ProcessNodeDto> list = nodeMap.get(MapUtils.getString(node, "typeId"));
                list.add(dto);
            } else {
                List<ProcessNodeDto> list = new ArrayList<>();
                list.add(dto);
                nodeMap.put(MapUtils.getString(node, "typeId"), list);
            }
        }
        nodeList = handleList(typeList, nodeMap);
        return nodeList;
    }

    /**
     * 获取模型节点列表
     *
     * @return 模型节点列表
     */
    public List<ProcessNodeDto> getTableNodeDto(String projectId) {
        List<ProcessNodeDto> nodeList;
        List<CommonTypeDTO> typeList = commonTypeService.findCommonType(CommonTypeEnums.DATATABLE, projectId);
        //去除表分类的顶级节点
        if (!typeList.isEmpty() && typeList.get(0).getChildren() != null) {
            typeList = typeList.get(0).getChildren();
        } else {
            typeList = new ArrayList<>();
        }
        List<Map<String, Object>> tableNodesList = this.mapper.getTableList(projectId);
        Map<String, List<ProcessNodeDto>> nodeMap = new HashMap<>();
        //处理流程节点
        for (Map<String, Object> node : tableNodesList) {
            ProcessNodeDto dto = new ProcessNodeDto();
            dto.setId(MapUtils.getString(node, "id"));
            dto.setName(MapUtils.getString(node, "name"));
            dto.setCode(TaskType.MODEL.name());
            dto.setIconName("model");
            dto.setColor("#00FF00");
            dto.setParams(null);
            //是节点
            dto.setIsNode(1);
            dto.setType(TaskType.MODEL.name());
            dto.setParams("{\n" +
                    "  \"name\": \"模型\",\n" +
                    "  \"bundle\": \"model\",\n" +
                    "  \"description\": \"模型节点\",\n" +
                    "  \"inportType\": \"DEFAULT\",\n" +
                    "  \"inports\": [\"Default\"],\n" +
                    "  \"inportsName\": [\"输入\"],\n" +
                    "  \"outportType\": \"DEFAULT\",\n" +
                    "  \"outports\": [\"Default\"],\n" +
                    "  \"outportsName\": [\"输出\"],\n" +
                    "  \"outportsFieldType\":\"Default\",\n" +
                    "  \"properties\": [\n" +
                    "  ]\n" +
                    "}");
            //模型节点
            dto.setAlgorithmType("1");
            if (nodeMap.containsKey(MapUtils.getString(node, "typeId"))) {
                List<ProcessNodeDto> list = nodeMap.get(MapUtils.getString(node, "typeId"));
                list.add(dto);
            } else {
                List<ProcessNodeDto> list = new ArrayList<>();
                list.add(dto);
                nodeMap.put(MapUtils.getString(node, "typeId"), list);
            }
        }
        nodeList = handleList(typeList, nodeMap);
        return nodeList;
    }

    /**
     * 查询所有的计算节点组件
     */
    public List<TaskNodes> getProcessNodeList() {
        TaskNodes query = new TaskNodes();
        query.setType("sparkFlow");
        query.setTreeNodeFlag(0);
        return this.selectList(query);
    }

    /**
     * 获取流程节点列表
     *
     * @return
     */
    public List<ProcessNodeDto> getProcessNodeDto(String type, Integer algoType) {
        //algoType  0--内置算法；1--phm自定义算法
        //处理所有的分类节点
        TaskNodes query = new TaskNodes();
        query.setTreeNodeFlag(1);
        List<TaskNodes> allTypeList = this.selectList(query);
        Map<String, TaskNodes> allTypeMap = new HashMap<>();
        for (TaskNodes nodes : allTypeList) {
            allTypeMap.put(nodes.getId(), nodes);
        }
        List<TaskNodes> taskNodesList = this.mapper.getProcessNodeList(type, algoType);
        Map<String, List<ProcessNodeDto>> nodeMap = new HashMap<>(16);
        List<TaskNodes> typeList = new ArrayList<>();
        //处理第一级目录
        for (TaskNodes node : taskNodesList) {
            if (0 == algoType) {
                if (node.getPid() != null && "-1".equals(node.getPid())) {
                    typeList.add(node);
                }
            } else {
                if (node.getPid() != null && "-2".equals(node.getPid())) {
                    typeList.add(node);
                }
            }

        }

        //处理第二级及其他流程节点
        for (TaskNodes node : taskNodesList) {
            if (node.getPid() == null || "-1".equals(node.getPid()) || "-2".equals(node.getPid())) {
                continue;
            }
            ProcessNodeDto dto = new ProcessNodeDto();
            dto.setId(node.getId());
            dto.setName(node.getName());
            dto.setCode(node.getCode());
            dto.setIconName(node.getIcon());
            dto.setColor(node.getColor());
            dto.setParams(node.getParams());
            dto.setOrderBy(node.getOrderBy());
            //dto.setRemark(node.getRemark());
            //是节点
            if (1 == node.getTreeNodeFlag()) {
                dto.setIsNode(0);
            } else {
                dto.setIsNode(1);
            }
            dto.setType(node.getCode());
            //算法节点
            dto.setAlgorithmType("0");
            //判断父节点是否存在
            if (!isExistInList(typeList, node.getPid())) {
                TaskNodes typeNode = allTypeMap.get(node.getPid());
                if (typeNode != null) {
                    typeList.add(typeNode);
                }
            }

            if (nodeMap.containsKey(node.getPid())) {
                List<ProcessNodeDto> list = nodeMap.get(node.getPid());
                list.add(dto);
            } else {
                List<ProcessNodeDto> list = new ArrayList<>();
                list.add(dto);
                nodeMap.put(node.getPid(), list);
            }
        }

        //排序
        typeList = typeList.stream()
                .sorted(Comparator.comparing(TaskNodes::getOrderBy, Comparator.nullsLast(Integer::compareTo)))
                .collect(Collectors.toList());

        List<ProcessNodeDto> nodeList = new ArrayList<>();
        for (TaskNodes typeNode : typeList) {
            ProcessNodeDto dto = new ProcessNodeDto();
            dto.setId(typeNode.getId());
            dto.setName(typeNode.getName());
            dto.setIconName(typeNode.getIcon());
            if (StringUtils.isBlank(typeNode.getParams())) {
                dto.setParams("{}");
            } else {
                dto.setParams(typeNode.getParams());
            }
            //是分类还是节点
            if (1 == typeNode.getTreeNodeFlag()) {
                dto.setIsNode(0);
            } else {
                dto.setIsNode(1);
            }

            nodeList.add(dto);
        }

        //处理树结构
        nodeList = handleProcessNodeTreeList(nodeList, nodeMap);

        return nodeList;
    }

    private boolean isExistInList(List<TaskNodes> typeList, String id) {
        boolean isExist = false;
        for (TaskNodes nodes : typeList) {
            if (StringUtils.isBlank(id) || nodes == null || StringUtils.isBlank(nodes.getId())) {
                continue;
            }
            if (id.equals(nodes.getId())) {
                isExist = true;
                return isExist;
            }
        }
        return isExist;
    }

    /**
     * 递归处理流程节点树
     *
     * @param typeList
     * @param nodeMap
     * @return
     */
    private List<ProcessNodeDto> handleProcessNodeTreeList(List<ProcessNodeDto> typeList, Map<String, List<ProcessNodeDto>> nodeMap) {

        for (ProcessNodeDto type : typeList) {
            List<ProcessNodeDto> nodeDtoList = nodeMap.get(type.getId());
            if (CollectionUtils.isNotEmpty(nodeDtoList)) {
                nodeDtoList = nodeDtoList.stream()
                        .sorted(Comparator.comparing(ProcessNodeDto::getOrderBy, Comparator.nullsLast(Integer::compareTo)))
                        .collect(Collectors.toList());
                handleProcessNodeTreeList(nodeDtoList, nodeMap);
                type.setChildren(nodeDtoList);

            } else {
                type.setChildren(null);
            }

        }
        return typeList;
    }


    /**
     * 处理流程节点树
     *
     * @param typeList
     * @param nodeMap
     * @return
     */
    private List<ProcessNodeDto> handleList(List<CommonTypeDTO> typeList, Map<String, List<ProcessNodeDto>> nodeMap) {
        List<ProcessNodeDto> nodeList = new ArrayList<>();
        for (CommonTypeDTO type : typeList) {
            ProcessNodeDto dto = new ProcessNodeDto();
            dto.setId(type.getId());
            dto.setName(type.getName());
            //dto.setIconName(type.getIcon());
            dto.setIconName("folder");
            dto.setParams("{}");
            //是分类
            dto.setIsNode(0);
            if (type.getChildren() != null && type.getChildren().size() > 0) {
                List<ProcessNodeDto> subList = handleList(type.getChildren(), nodeMap);
                dto.setChildren(subList);
            } else {
                if (nodeMap.containsKey(type.getId())) {
                    List<ProcessNodeDto> nodeDtoList = nodeMap.get(type.getId());
                    nodeDtoList = nodeDtoList.stream()
                            .sorted(Comparator.comparing(ProcessNodeDto::getOrderBy, Comparator.nullsLast(Integer::compareTo)))
                            .collect(Collectors.toList());
                    dto.setChildren(nodeDtoList);
                }
            }
            nodeList.add(dto);
        }
        return nodeList;
    }


    public List<ProcessNodeDto> getProcessModelList(String projectId) {
        return getTableNodeDto(projectId);
    }

    public ProcessNodeDto getNodeInfoById(String id) {
        ProcessNodeDto node = new ProcessNodeDto();

        TaskNodes taskNodes = this.mapper.selectByPrimaryKey(id);
        if (taskNodes == null) {
            AlgoNodes algoNodes = algoNodesService.selectByPrimaryKey(id);
            if (algoNodes == null) {
                throw new RuntimeException("该节点不存在");
            }
            node.setId(algoNodes.getId());
            node.setName(algoNodes.getName());
            node.setRemark(algoNodes.getRemark());
        } else {
            node.setId(taskNodes.getId());
            node.setName(taskNodes.getName());
            node.setRemark(taskNodes.getRemark());
        }


        return node;
    }

    /**
     * 查询分类树
     *
     * @param algoType 0:内置算法、1:自定义算法、2:其他
     * @param showed   0：不显示，1：显示
     * @return
     */
    public List<TaskNodes> getTreeNode(Integer algoType, Integer showed) {
        // step1:合法性检查
        checkGetTreeNodeValidity(algoType, showed);

        // step2:查询分类树
        return getTreeNodeInner(algoType, showed);
    }

    /**
     * 查询指定分类下算法节点列表
     *
     * @param id
     * @param name
     * @param remark
     * @param pageNo
     * @param pageSize
     * @param secretLevel
     * @return
     */
    public QueryRespBean<TaskNodes> getAlgoNodeList(String id, String name, String remark,
                                                    Integer pageNo, Integer pageSize, String secretLevel) {
        CheckParaUtils.checkStringValidity(id, logger, "节点ID为null，查询算法节点失败.");

        return getAlgoNodeInner(id, name, remark, pageNo, pageSize, secretLevel);
    }

    /**
     * 查看算法节点详情
     *
     * @param id
     * @return
     */
    public TaskNodes getAlgoNodeInfo(String id) {
        TaskNodes queryRet = queryTaskNodeById(id);
        if (queryRet == null) {
            throw new BusinessException("未找到节点信息.");
        }
        if (!"-1".equals(id) && !"-2".equals(id)) {
            valueCovert(Arrays.asList(queryRet));
        }

        BdpLogUtil.log4Query(queryRet);

        return queryRet;
    }

    /**
     * 新增分类节点
     *
     * @param taskNodes
     * @return
     */
    public Boolean insertTypeNode(TaskNodes taskNodes) {
        // step1:合法性检查
        checkInsertTypeNodeValidity(taskNodes);

        // step2:保存到DB
        return insertTypeNodeInner(taskNodes);
    }

    /**
     * 更新分类节点
     *
     * @param taskNodes
     * @return
     */
    public Boolean updateTypeNode(TaskNodes taskNodes) {
        // step1:合法性检查
        checkInsertTypeNodeValidity(taskNodes);

        // step2:保存到DB
        return updateNodeInner(taskNodes);
    }

    /**
     * 新增分类节点
     *
     * @param taskNodes
     * @return
     */
    public Boolean insertAlgoNode(TaskNodes taskNodes) {
        // step1:合法性检查
        checkInsertAlgoNodeValidity(taskNodes);

        // step2:保存到DB
        return insertAlgoNodeInner(taskNodes);
    }

    /**
     * 更新分类节点
     *
     * @param taskNodes
     * @return
     */
    public Boolean updateAlgoNode(TaskNodes taskNodes) {
        // step1:合法性检查
        checkInsertAlgoNodeValidity(taskNodes);

        // step2:保存到DB
        return updateNodeInner(taskNodes);
    }

    /**
     * 删除节点
     *
     * @param id
     * @return
     */
    public Boolean deleteNode(String id) {
        if (StringUtils.isEmpty(id)) {
            return true;
        }

        List<String> ids = Arrays.asList(id.trim().split(Constants.COMMA));
        for (String nodeId : ids) {
            deleteSingleNode(nodeId);
        }

        return true;
    }

    /**
     * 上传算法文件
     *
     * @param file
     * @return 返回remark、settingJson信息
     */
    public TaskNodes uploadAlgoFile(MultipartFile file) {
        // step1:合法性检查
        checkUploadValidity(file);

        // step2:解析算法文件
        return parseAlgoFile(file);
    }

    /**
     * 查询指定分类下算法节点列表
     *
     * @param id 分类ID
     * @return
     */
    public List<TaskNodes> listAlgoNodeByType(String id) {
        CheckParaUtils.checkStringValidity(id, logger, "节点ID为null，查询算法节点失败.");

        return searchTreeNodeList(TaskNodes.builder().pid(id).treeNodeFlag(0).build());
    }

    /**
     * 查询分类树
     *
     * @param algoType 0:内置算法、1:自定义算法、2:其他
     * @param showed   0：不显示，1：显示
     * @return
     */
    public List<TaskNodes> getAllNodes(Integer algoType, Integer showed) {
        // step1:合法性检查
        checkGetTreeNodeValidity(algoType, showed);

        // step2:查询分类树
        return getAllNodesInner(algoType, showed);
    }

    /**
     * 生成json文件
     */
    public void generatePythonFile() {
        TaskNodes query = new TaskNodes();
        query.setProgramType(5);
        query.setCode("SPARKFLOW");
        List<TaskNodes> list = this.selectList(query);
        String algorithmPath = ConfigUtils.getInstance().getString("piflow.algorithm.path", "/phm/plugins/algorithm");
        File path = new File(algorithmPath);
        if (!path.exists()) {
            path.mkdirs();
        }
        String baseContent = FileUtils.readResourceFile("/template/CommonProcessNode.py");
        for (TaskNodes node : list) {
            if (StringUtils.isNotBlank(node.getContent())) {
                String script = node.getContent();
                script = script.replaceAll("\n", "\r\n");
                String content = baseContent.replaceAll("###script###", script);
                String filePath = algorithmPath + "/" + node.getId() + ".py";
                try {
                    FileUtils.writeStringToFile(new File(filePath), content, "utf-8");
                } catch (IOException e) {
                    e.printStackTrace();
                }
                node.setNodeFilePath(node.getId() + ".py");
                this.updateByPrimaryKey(node);
            }
        }
    }

    /**
     * 解析算法文件，获取指定参数
     *
     * @param file
     * @return
     */
    private TaskNodes parseAlgoFile(MultipartFile file) {
        String localTempFile = null;
        try {
            // step1:算法文件保存到本地
            localTempFile = saveLocalTempFile(file);

            // step2:jep调用.py文件，获取指定参数(remark/setting)
            Map<String, Object> map = parseParaFromAlgoFile(localTempFile);

            // step3:填充算法预览内容
            String content = org.apache.commons.io.FileUtils.readFileToString(new File(localTempFile), Charset.defaultCharset());

            return TaskNodes.builder()
                    .content(content)
                    .remark(map.get(REMARK_KEY_NAME).toString())
                    .params(map.get(SETTING_KEY_NAME).toString()).build();
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            // step4:删除临时文件
            deleteLocalTempFile(localTempFile);
        }
    }

    /**
     * 查询分类树合法性检查
     *
     * @param algoType
     * @param showed
     */
    private void checkGetTreeNodeValidity(Integer algoType, Integer showed) {
        if (algoType < 0 || algoType > 2) {
            throw new BusinessException(String.format("算法分类为[%s]，仅支持[0、1、2]，查询分类树失败.", algoType));
        }

        if (showed != 0 && showed != 1) {
            throw new BusinessException(String.format("显示状态为[%s]，仅支持[0、1]，查询分类树失败.", showed));
        }
    }

    /**
     * 新增分类节点合法性检查
     *
     * @param taskNodes
     */
    private void checkInsertTypeNodeValidity(TaskNodes taskNodes) {
        CheckParaUtils.checkObjectValidity(taskNodes, logger, "入参为空，新增分类节点失败.");
        CheckParaUtils.checkStringValidity(taskNodes.getPid(), logger, "父节点ID为空，新增分类节点失败.");
        CheckParaUtils.checkStringValidity(taskNodes.getName(), logger, "节点名称为空，新增分类节点失败.");
        CheckParaUtils.checkObjectValidity(taskNodes.getAlgoType(), logger, "算法类型为空，新增分类节点失败.");
        if (taskNodes.getAlgoType() < 0 || taskNodes.getAlgoType() > 2) {
            throw new BusinessException(String.format("算法分类为[%s]，仅支持[0、1、2]，查询分类树失败.", taskNodes.getAlgoType()));
        }
    }

    /**
     * 新增算法节点合法性检查
     *
     * @param taskNodes
     */
    private void checkInsertAlgoNodeValidity(TaskNodes taskNodes) {
        CheckParaUtils.checkObjectValidity(taskNodes, logger, "入参为空，新增分类节点失败.");
        CheckParaUtils.checkStringValidity(taskNodes.getPid(), logger, "分类节点ID为空，新增分类节点失败.");
        CheckParaUtils.checkStringValidity(taskNodes.getName(), logger, "节点名称为空，新增分类节点失败.");
        CheckParaUtils.checkStringValidity(taskNodes.getType(), logger, "节点分类为空，新增分类节点失败.");
        CheckParaUtils.checkStringValidity(taskNodes.getIcon(), logger, "节点图标为空，新增分类节点失败.");
        CheckParaUtils.checkObjectValidity(taskNodes.getProgramType(), logger, "算法类型为空，新增分类节点失败.");

        // 算法类型校验；[0,1,2,5]
        if (!((taskNodes.getProgramType() >= 0 && taskNodes.getProgramType() <= 2)
                || (taskNodes.getProgramType() == 5))) {
            throw new BusinessException("算法类型取值范围[0,1,2,5]");
        }
    }

    /**
     * 上传算法文件合法性检查
     *
     * @param file
     */
    private void checkUploadValidity(MultipartFile file) {
        CheckParaUtils.checkObjectValidity(file, logger, "算法文件为空，上传文件失败.");

        String fileName = file.getOriginalFilename();
        if ((fileName == null) || (!fileName.endsWith(UPLOAD_FILE_END_WITH))) {
            throw new BusinessException(String.format("算法文件[%s]格式不符合，仅支持.py格式", file.getName()));
        }
    }

    /**
     * 根据指定条件查询所有分类树节点
     *
     * @param algoType
     * @param showed
     * @return
     */
    private List<TaskNodes> getAllTreeNodes(Integer algoType, Integer showed, Integer treeNodeFlag) {
        TaskNodes queryParam = TaskNodes.builder()
                .algoType(algoType) // 自定义算法、内置算法
                .showed(showed)     // 0:不显示;1:显示
                .treeNodeFlag(treeNodeFlag)    // 0:算法节点；1：分类节点
                .type(TASK_NODE_TYPE)
                .build();

        return queryNodes(queryParam);
    }

    /**
     * 组装分类树
     *
     * @param allTaskNodes
     * @param algoType
     * @return
     */
    private List<TaskNodes> constructTree(List<TaskNodes> allTaskNodes, Integer algoType) {
        List<TaskNodes> tempNodeList = new ArrayList<>();
        Map<String, List<TaskNodes>> tempNodeMap = new HashMap<>();
        String pId = algoType == 0 ? "-1" : "-2";
        for (TaskNodes taskNode : allTaskNodes) {
            if (taskNode.getPid() != null && taskNode.getPid().equals(pId)) {
                tempNodeList.add(taskNode);
                continue;
            }

            if (tempNodeMap.containsKey(taskNode.getPid())) {
                tempNodeMap.get(taskNode.getPid()).add(taskNode);
            } else {
                List<TaskNodes> list = new ArrayList<>();
                list.add(taskNode);
                tempNodeMap.put(taskNode.getPid(), list);
            }
        }

        setChildrenNode(tempNodeList, tempNodeMap);

        TaskNodes rootNode = null;
        if (algoType == 0) {// 内置算法根节点
            rootNode = queryTaskNodeById("-1");
        } else if (algoType == 1) {// 自定义算法根节点
            rootNode = queryTaskNodeById("-2");
        } else {
            throw new BusinessException("当前仅支持内置算法、自定义算法两种,algoType取值范围[0,1]");
        }
        rootNode.setChildren(tempNodeList);

        return Arrays.asList(rootNode);
    }

    /**
     * 设置TaskNode子节点信息
     *
     * @param taskNodeList
     * @param taskNodeMap
     */
    private void setChildrenNode(List<TaskNodes> taskNodeList, Map<String, List<TaskNodes>> taskNodeMap) {
        for (TaskNodes taskNode : taskNodeList) {
            List<TaskNodes> childrenTaskNodeList = taskNodeMap.get(taskNode.getId());
            if (CollectionUtils.isNotEmpty(childrenTaskNodeList)) {
                setChildrenNode(childrenTaskNodeList, taskNodeMap);
                taskNode.setChildren(childrenTaskNodeList);
            } else {
                taskNode.setChildren(null);
            }
        }
    }

    /**
     * 查询分类树内部处理逻辑
     *
     * @param algoType
     * @param showed
     * @return
     */
    private List<TaskNodes> getTreeNodeInner(Integer algoType, Integer showed) {
        // step1:查询所有分类节点
        List<TaskNodes> allTaskNodes = getAllTreeNodes(algoType, showed, 1);

        // step2:组装分类树，返回结果
        return constructTree(allTaskNodes, algoType);
    }

    /**
     * 新增分类节点
     *
     * @param taskNodes
     */
    private Boolean insertTypeNodeInner(TaskNodes taskNodes) {
        // step1:查询父节点信息
        TaskNodes parentNode = queryTaskNodeById(taskNodes.getPid());
        if (parentNode == null) {
            throw new BusinessException(String.format("父节点[%s]不存在，新增分类节点失败", taskNodes.getPid()));
        }

        // step2:持久化到DB
        taskNodes.setId(ComUtil.getId());
        taskNodes.setCode(DEFAULT_CODE);
        taskNodes.setTreeNodeFlag(1);// 0：算法节点，1：分类节点
        taskNodes.setType(parentNode.getType());
        // taskNodes.setIcon(DEFAULT_ICON_FOLDER); // 前端传
        taskNodes.setShowed(taskNodes.getShowed() == null ? 1 : taskNodes.getShowed());
        this.insert(taskNodes);

        // step3:记录系统日志
        BdpLogUtil.log4Insert(taskNodes);

        return true;
    }

    /**
     * 新增算法节点
     *
     * @param taskNodes
     */
    private Boolean insertAlgoNodeInner(TaskNodes taskNodes) {
        // step1:查询父节点信息
        TaskNodes parentNode = queryTaskNodeById(taskNodes.getPid());
        if (parentNode == null) {
            throw new BusinessException(String.format("父节点[%s]不存在，新增分类节点失败", taskNodes.getPid()));
        }

        // step2:持久化到DB
        taskNodes.setId(ComUtil.getId());
        taskNodes.setCode(DEFAULT_CODE);
        taskNodes.setAlgoType(parentNode.getAlgoType());
        taskNodes.setTreeNodeFlag(0);// 0：算法节点，1：分类节点
        taskNodes.setShowed(taskNodes.getShowed() == null ? 1 : taskNodes.getShowed());// 0：不显示，1：显示
        PojoUtil.setSysProperties(taskNodes, PlatformConstant.OpType.insert);// 设置平台相关参数
        this.insert(taskNodes);

        // step3:记录系统日志
        BdpLogUtil.log4Insert(taskNodes);

        return true;
    }

    /**
     * 更新分类节点
     *
     * @param taskNodes
     * @return
     */
    private Boolean updateNodeInner(TaskNodes taskNodes) {
        // step1:查询节点是否存在
        TaskNodes oldTaskNode = queryTaskNodeById(taskNodes.getId());
        if (oldTaskNode == null) {
            throw new BusinessException(String.format("未找到节点信息[%s]，更新失败.", taskNodes.getId()));
        }

        // step2:更新DB
        this.updateByPrimaryKeySelective(taskNodes);

        // step3:记录系统日志
        BdpLogUtil.log4Update(taskNodes, oldTaskNode);

        return true;
    }

    /**
     * 查询指定分类下节点信息
     *
     * @param id
     * @param name
     * @param remark
     * @param pageNo
     * @param pageSize
     * @param secretLevel
     * @return
     */
    private QueryRespBean<TaskNodes> getAlgoNodeInner(String id, String name, String remark,
                                                      Integer pageNo, Integer pageSize, String secretLevel) {
        QueryRespBean<TaskNodes> queryRespBean = new QueryRespBean<>();

        TaskNodes typeNode = getAlgoNodeInfo(id);
        if (typeNode == null) {
            return queryRespBean;
        }

        List<String> wordSecretList = new ArrayList<>();
        if (StringUtils.isNotEmpty(secretLevel)) {
            wordSecretList.addAll(this.sysLookupClient.getSecretWordList(secretLevel));
        } else {
            wordSecretList = ThreadContextHelper.getWordSecretList();
        }

        TaskNodes queryParam = TaskNodes.builder()
                .pid(id)
                .treeNodeFlag(0)
                .algoType(typeNode.getAlgoType())
                .name(name)
                .remark(remark)
                .build();
        queryParam.setWordSecretList(wordSecretList);
        queryRespBean.setResult(searchTreeNodeByPage(queryParam, pageNo, pageSize));
        if (queryRespBean.getResult().getResult() != null) {
            valueCovert(queryRespBean.getResult().getResult());
        }

        BdpLogUtil.log4Query(queryRespBean);

        return queryRespBean;
    }

    /**
     * 根据父节点查询子节点列表
     *
     * @param rootNode
     * @return
     */
    private List<TaskNodes> queryTypeNodeByParent(TaskNodes rootNode) {
        List<TaskNodes> result = new ArrayList<>();
        if (rootNode == null) {
            return result;
        }

        TaskNodes queryParam = TaskNodes.builder()
                .pid(rootNode.getId())
                .algoType(rootNode.getAlgoType())
                .showed(rootNode.getShowed())
                .type(TASK_NODE_TYPE)
                .build();

        return queryNodes(queryParam);
    }

    /**
     * 根据查询条件获取任务列表
     *
     * @param queryParam
     * @return
     */
    private List<TaskNodes> queryNodes(TaskNodes queryParam) {
        List<TaskNodes> taskNodesList = searchTreeNodeList(queryParam);
        if (CollectionUtils.isEmpty(taskNodesList)) {
            return new ArrayList<>();
        }

        return taskNodesList;
    }

    /**
     * 删除单个节点
     * 1、分类节点，需要判断是否有子节点，如果有则不允许删除
     * 2、普通算法节点，直接删除
     *
     * @param id
     * @return
     */
    private Boolean deleteSingleNode(String id) {
        if (StringUtils.isEmpty(id)) {
            return true;
        }

        // step1:根据ID查询节点信息
        TaskNodes queryRet = queryTaskNodeById(id);
        if (queryRet == null) {
            return true;
        }

        // step2:分类节点，含有子节点则不允许删除
        if (queryRet.getTreeNodeFlag() == 1) {
            List<TaskNodes> children = this.queryTypeNodeByParent(queryRet);
            if (CollectionUtils.isNotEmpty(children)) {
                throw new BusinessException("请先删除子节点!");
            }
        }
        deleteTaskNode(id);

        // step4:记录审计日志
        BdpLogUtil.log4Delete(queryRet);

        return true;
    }

    /**
     * 设置创建人、密级信息
     *
     * @param taskNodes
     */
    private void valueCovert(List<TaskNodes> taskNodes) {
        //循环组装请求数据
        Map<String, Set<String>> convertFormData = new HashMap<>();
        for (TaskNodes node : taskNodes) {
            BusinessUtil.createConvertSet(convertFormData, SystemConstant.USER, node.getCreatedBy());
            BusinessUtil.createConvertSet(convertFormData, avicit.bdp.core.constant.Constants.BDP_DATA_SECRET_LEVEL_KEY, node.getSecretLevel());

            fillTaskNodesInfo(node);
        }
        if (convertFormData.size() > 0) {
            //获取请求结果
            Map<String, Map<String, String>> convertResultData = this.convertColumnClient.replace(convertFormData);
            //循环设置Alias或Name的值
            for (TaskNodes node : taskNodes) {
                node.setCreatedBy(BusinessUtil.convertFormat(convertResultData, SystemConstant.USER, node.getCreatedBy()));
                node.setSecretLevelName(BusinessUtil.convertFormat(convertResultData, avicit.bdp.core.constant.Constants.BDP_DATA_SECRET_LEVEL_KEY, node.getSecretLevel()));
            }
        }
    }

    /**
     * 保存上传文件到本地
     *
     * @param file
     * @return
     */
    private String saveLocalTempFile(MultipartFile file) {
        String fileName = file.getOriginalFilename() + ComUtil.getId();
        try {
            org.apache.commons.io.FileUtils.writeByteArrayToFile(new File(fileName), file.getBytes());
            return fileName;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(String.format("写入本地临时文件[%s]失败，上传算法文件失败.", fileName));
        }
    }

    /**
     * 删除临时文件
     *
     * @param fileName
     */
    private void deleteLocalTempFile(String fileName) {
        if (StringUtils.isEmpty(fileName)) {
            return;
        }

        File file = new File(fileName);
        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 填充节点相关字段
     *
     * @param taskNode
     */
    private void fillTaskNodesInfo(TaskNodes taskNode) {
        // 编程类型;0:java-spark; 1:scala-spark;2:python-spark;5:python
        if (taskNode.getProgramType() == null) {
            taskNode.setProgramTypeName("python");
        } else {
            if (taskNode.getProgramType() == 0) {
                taskNode.setProgramTypeName("java");
            } else if (taskNode.getProgramType() == 1) {
                taskNode.setProgramTypeName("scala");
            } else if (taskNode.getProgramType() == 2) {
                taskNode.setProgramTypeName("python");
            } else if (taskNode.getProgramType() == 5) {
                taskNode.setProgramTypeName("python");
            }
        }

        // 所属关系
        if (taskNode.getAlgoType() == null) {
            taskNode.setRelation(BUILT_IN_ALGO);
        } else {
            if (taskNode.getAlgoType() == 0) {
                taskNode.setRelation(BUILT_IN_ALGO);
            } else if (taskNode.getAlgoType() == 1) {
                taskNode.setRelation(CUSTOMIZED_ALGO);
            } else {
                taskNode.setRelation(OTHER_ALGO);
            }
        }

        // 所属分类
        TaskNodes queryRet = selectByPrimaryKey(taskNode.getPid());
        if (queryRet != null) {
            taskNode.setTypeName(queryRet.getName());
        }
    }

    /**
     * 从算法文件中解析参数
     *
     * @param fileName
     * @return
     */
    private Map<String, Object> parseParaFromAlgoFile(String fileName) {
        Map<String, Object> map = new HashMap<>();
        StringBuilder setSb = new StringBuilder();
        StringBuilder remarkSb = new StringBuilder();

        try (BufferedReader reader = new BufferedReader(new FileReader(new File(fileName)))) {
            boolean setFlag = false;
            boolean remarkFlag = false;
            String line = null;
            while ((line = reader.readLine()) != null) {
                if (line.contains(SETTING_KEY_NAME)) {
                    setFlag = true;
                }

                if (line.contains(REMARK_KEY_NAME)) {
                    setFlag = false;
                    remarkFlag = true;
                }

                if (setFlag) {
                    setSb.append(line);
                }

                if (remarkFlag) {
                    remarkSb.append(line);
                }
            }
        } catch (Exception e) {
            throw new BusinessException(String.format("解析[%s]错误,errMsg=%s", fileName, e.getMessage()));
        }

        if (!StringUtils.isEmpty(remarkSb.toString())) {
            String[] remarkStrArr = remarkSb.toString().trim().split("\"\"\"");
            if (remarkStrArr.length != 3) {
                throw new BusinessException("文件格式有误，getRemark方法格式为:\n" + "def getRemark():\n" +
                        "    remark = \"\"\" \"\"\"\n" +
                        "\treturn remark");
            }
            map.put(REMARK_KEY_NAME, remarkStrArr[1]);
        } else {
            map.put(REMARK_KEY_NAME, null);
        }

        if (!StringUtils.isEmpty(setSb.toString())) {
            String[] setStrArr = setSb.toString().trim().split("\"\"\"");
            if (setStrArr.length != 3) {
                throw new BusinessException("文件格式有误，getSettingJson方法格式为:\n" + "def getSettingJson():\n" +
                        "    setting = \"\"\" \"\"\"\n" +
                        "\treturn setting");
            }
            map.put(SETTING_KEY_NAME, JSON.parse(setStrArr[1].trim()).toString());
        } else {
            map.put(SETTING_KEY_NAME, null);
        }

        return map;
    }

    /**
     * 查询分类树内部处理逻辑
     *
     * @param algoType
     * @param showed
     * @return
     */
    private List<TaskNodes> getAllNodesInner(Integer algoType, Integer showed) {
        // step1:查询所有分类节点、算法节点
        List<TaskNodes> queryAllTreeNodes = getAllTreeNodes(algoType, showed, 1);
        List<TaskNodes> queryAllAlgoNodes = getAllTreeNodes(algoType, showed, 0);

        // step3:构建<分类节点ID， 节点列表>MAP
        Map<String, List<TaskNodes>> algoNodeMap = buildTaskNodeMap(queryAllAlgoNodes);

        // step3:组装分类树，包括分类节点、算法节点
        return constructAlgoTree(queryAllTreeNodes, algoNodeMap, algoType);
    }

    /**
     * 构建<分类节点ID， 节点列表>MAP
     *
     * @param allAlgoNodes
     * @return
     */
    private Map<String, List<TaskNodes>> buildTaskNodeMap(List<TaskNodes> allAlgoNodes) {
        Map<String, List<TaskNodes>> algoNodeMap = new HashMap<>();

        for (TaskNodes taskNode : allAlgoNodes) {
            if (StringUtils.isEmpty(taskNode.getPid())) {
                continue;
            }

            List<TaskNodes> childrenNodeList = algoNodeMap.get(taskNode.getPid());
            if (CollectionUtils.isNotEmpty(childrenNodeList)) {
                childrenNodeList.add(taskNode);
                continue;
            }

            List<TaskNodes> newList = new ArrayList<>();
            newList.add(taskNode);
            algoNodeMap.put(taskNode.getPid(), newList);
        }

        return algoNodeMap;
    }

    /**
     * 每个分类节点加上算法节点
     *
     * @param allTreeNodes
     * @param algoNodeMap
     * @param algoType
     */
    private List<TaskNodes> constructAlgoTree(List<TaskNodes> allTreeNodes,
                                              Map<String, List<TaskNodes>> algoNodeMap,
                                              Integer algoType) {
        List<TaskNodes> tempNodeList = new ArrayList<>();
        Map<String, List<TaskNodes>> tempNodeMap = new HashMap<>();
        String pId = algoType == 0 ? "-1" : "-2";
        for (TaskNodes taskNode : allTreeNodes) {
            if (taskNode.getPid() != null && taskNode.getPid().equals(pId)) {
                tempNodeList.add(taskNode);
                continue;
            }

            if (tempNodeMap.containsKey(taskNode.getPid())) {
                tempNodeMap.get(taskNode.getPid()).add(taskNode);
            } else {
                List<TaskNodes> list = new ArrayList<>();
                list.add(taskNode);
                tempNodeMap.put(taskNode.getPid(), list);
            }
        }

        setAlgoChildrenNode(tempNodeList, tempNodeMap, algoNodeMap);

        TaskNodes rootNode = queryTaskNodeById(pId);
        rootNode.setChildren(tempNodeList);

        return Arrays.asList(rootNode);
    }

    /**
     * 设置TaskNode子节点信息
     *
     * @param taskNodeList
     * @param taskNodeMap
     */
    private void setAlgoChildrenNode(List<TaskNodes> taskNodeList,
                                     Map<String, List<TaskNodes>> taskNodeMap,
                                     Map<String, List<TaskNodes>> algoNodeMap) {
        for (TaskNodes taskNode : taskNodeList) {
            List<TaskNodes> childrenTaskNodeList = taskNodeMap.get(taskNode.getId());
            if (CollectionUtils.isNotEmpty(childrenTaskNodeList)) {
                setAlgoChildrenNode(childrenTaskNodeList, taskNodeMap, algoNodeMap);
                taskNode.setChildren(childrenTaskNodeList);
            } else {
                taskNode.setChildren(algoNodeMap.get(taskNode.getId()));
            }
        }
    }

    /**
     * 分页查询节点列表
     *
     * @param queryBean
     * @param pageNo
     * @param pageSize
     * @return
     */
    public Page<TaskNodes> searchTreeNodeByPage(TaskNodes queryBean, Integer pageNo, Integer pageSize) {
        if (queryBean == null) {
            return new Page<>();
        }

        PageHelper.startPage(pageNo, pageSize);
        Page<TaskNodes> taskNodes = this.mapper.searchTreeNodeByPage(queryBean, queryBean.getWordSecretList());
        if (taskNodes == null) {
            return new Page<>();
        }

        return taskNodes;
    }

    /**
     * 查询所有符合条件的节点信息
     *
     * @param queryBean
     * @return
     */
    public List<TaskNodes> searchTreeNodeList(TaskNodes queryBean) {
        if (queryBean == null) {
            return new ArrayList<>();
        }

        List<TaskNodes> taskNodes = this.mapper.searchTreeNodeList(queryBean);
        if (taskNodes == null) {
            return new ArrayList<>();
        }

        return taskNodes;
    }

    /**
     * 单个删除/批量删除节点
     *
     * @param id
     * @return
     */
    public Boolean deleteTaskNode(String id) {
        if (StringUtils.isEmpty(id)) {
            return true;
        }

        List<String> ids = Arrays.asList(id.trim().split(Constants.COMMA));
        for (String taskNodeId : ids) {
            this.deleteByPrimaryKey(taskNodeId);
        }

        return true;
    }

    /**
     * 根据ID查询节点信息
     *
     * @param id
     * @return
     */
    public TaskNodes queryTaskNodeById(String id) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }

        return this.selectByPrimaryKey(id);
    }

    /**
     * 查看项目类型
     *
     * @param projectType，默认为null
     * @param authType，是否应用授权，0：不参与授权，1：参与授权
     * @return
     */
    public List<ProjectType> getProcessTypeList(String projectType, Integer authType) {
        return this.projectTypeService.getProcessTypeList(projectType, authType == null ? 1 : 0);
    }

    /**
     * 设置应用范围
     *
     * @param projectTypeId
     * @param taskNodeIds
     * @param algoType
     * @return
     */
    public boolean setScope(String projectTypeId, String taskNodeIds, Integer algoType) {
        // step1:合法性检查
        checkSetScopeValidity(projectTypeId, taskNodeIds);
        CheckParaUtils.checkObjectValidity(algoType, logger, "算法类型为空，新增分类节点失败.");
        if (algoType < 0 || algoType > 1) {
            throw new BusinessException("算法类型取值范围为[0,1]，algoType=" + algoType);
        }

        // step2:根据projectTypeId清理旧数据
        cleanOldTaskNodeType(projectTypeId, algoType);

        // step3:构建节点与映射关系
        batchInsertTaskNodeType(projectTypeId, taskNodeIds, algoType);

        String logTitle = "应用范围设置";
        BdpLogUtil.log("通用算法库模块", logTitle, PlatformConstant.OpType.update);
        return true;
    }

    /**
     * 查询指定应用下节点列表
     *
     * @param scopeId
     * @param algoType
     * @return
     */
    public List<String> getTaskNodesByScope(String scopeId, Integer algoType) {
        CheckParaUtils.checkStringValidity(scopeId, logger, "未指定应用范围ID");
        ProjectType projectType = this.projectTypeService.selectByPrimaryKey(scopeId);
        CheckParaUtils.checkObjectValidity(projectType, logger, "未找到应用，id=" + scopeId);

        List<String> idList = new ArrayList<>();
        TaskNodeType queryParam = TaskNodeType.builder().type(projectType.getProcessType()).algoType(algoType).build();
        List<TaskNodeType> taskNodeTypeList = this.taskNodeTypeService.searchNodeTypeList(queryParam);
        if (taskNodeTypeList == null) {
            return idList;
        }
        for (TaskNodeType node : taskNodeTypeList) {
            idList.add(node.getNodeId());
        }
        idList = idList.stream().distinct().collect(Collectors.toList());

        return idList;
    }

    /**
     * 设置应用范围合法性检查
     *
     * @param projectTypeId
     * @param taskNodeIds
     */
    private void checkSetScopeValidity(String projectTypeId, String taskNodeIds) {
        CheckParaUtils.checkStringValidity(projectTypeId, logger, "项目类型为null.");

        ProjectType queryRet = this.projectTypeService.selectByPrimaryKey(projectTypeId);
        CheckParaUtils.checkObjectValidity(queryRet, logger, "未找到应用，id=" + projectTypeId);
    }

    /**
     * 清理<项目类型，算法节点>映射关系
     *
     * @param projectTypeId
     * @param algoType
     */
    private void cleanOldTaskNodeType(String projectTypeId, Integer algoType) {
        ProjectType queryRet = this.projectTypeService.selectByPrimaryKey(projectTypeId);
        if (queryRet != null) {
            this.taskNodeTypeService.deleteByCond(null, null,
                    queryRet.getProcessType(), null, algoType);
        }
    }

    /**
     * 批量插入<项目类型，算法节点>映射关系
     *
     * @param projectTypeId
     * @param taskNodeIds
     * @param algoType
     */
    private void batchInsertTaskNodeType(String projectTypeId, String taskNodeIds, Integer algoType) {
        if (StringUtils.isEmpty(taskNodeIds)) {
            return;
        }

        ProjectType projectType = this.projectTypeService.selectByPrimaryKey(projectTypeId);
        for (String nodeId : taskNodeIds.trim().split(avicit.bdp.core.constant.Constants.COMMA)) {
            TaskNodes queryRet = this.selectByPrimaryKey(nodeId);
            if (queryRet == null) {
                continue;
            }

            TaskNodeType taskNodeType = TaskNodeType.builder()
                    .id(ComUtil.getId())
                    .nodeId(nodeId)
                    .type(projectType.getProcessType())
                    .orderBy(queryRet.getOrderBy())
                    .algoType(algoType)
                    .build();

            this.taskNodeTypeService.insert(taskNodeType);
        }
    }

    /**
     * 删除节点与类型映射关系
     *
     * @param taskNodes
     * @return
     */
    private Boolean deleteNodeType(TaskNodes taskNodes) {
        this.taskNodeTypeService.deleteByCond(null, taskNodes.getId(), null, null, null);
        return true;
    }
}
